use crate::{
    math::Vector4f,
    function::render::{ loader::ShaderLoader, rhi::Rhi, render_target::RenderTarget},
};

use wgpu::{util::DeviceExt, CommandEncoder, TextureView};


pub enum BufferType {
    Vertex,
    Index,
}


const OUTPUT_VERTEX: [Vector4f; 4] = [
    Vector4f {
        x: -1.0,
        y: -1.0,
        z: -1.0,
        w: -1.0,
    },
    Vector4f {
        x: -1.0,
        y: 1.0,
        z: -1.0,
        w: 1.0,
    },
    Vector4f {
        x: 1.0,
        y: 1.0,
        z: 1.0,
        w: 1.0,
    },
    Vector4f {
        x: 1.0,
        y: -1.0,
        z: 1.0,
        w: -1.0,
    },
];


#[derive(Debug)]
pub struct WebgpuMesh {
    pub vertex_count: usize,
    pub vertex_buffer: wgpu::Buffer,
    pub indics_count: usize,
    pub index_buffer: wgpu::Buffer,
}

impl WebgpuMesh {
    pub fn get_vertex_layout<'a>() -> wgpu::VertexBufferLayout<'a> {
        wgpu::VertexBufferLayout {
            array_stride: std::mem::size_of::<Vector4f>() as wgpu::BufferAddress,
            step_mode: wgpu::VertexStepMode::Vertex,
            attributes: &[wgpu::VertexAttribute {
                offset: 0 as wgpu::BufferAddress,
                shader_location: 0,
                format: wgpu::VertexFormat::Float32x4,
            }],
        }
    }
}

const OUTPUT_INDEX: [u16; 6] = [0, 1, 2, 0, 2, 3];

pub struct OutputPass {
    graphic_pipeline: wgpu::RenderPipeline,
    index_buffer: wgpu::Buffer,
    vertex_buffer: wgpu::Buffer,
}

impl OutputPass {
    pub fn new(rhi: Rhi) -> Self {
        let shader = include_str!("output.wgsl");

        let shader = ShaderLoader::load(&rhi.borrow().device, shader);

        let mut group_layouts = Vec::with_capacity(1);

        // camera
        let canvas_batch_group_layout = RenderTarget::get_render_attachment_layout(rhi.clone());
        group_layouts.push(&canvas_batch_group_layout);
        let layout = rhi
            .borrow()
            .device
            .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
                label: Some("output Pass Layout"),
                bind_group_layouts: group_layouts.as_slice(),
                push_constant_ranges: &[],
            });

        let primitive = wgpu::PrimitiveState {
            topology: wgpu::PrimitiveTopology::TriangleList,
            strip_index_format: None,
            front_face: wgpu::FrontFace::Ccw,
            cull_mode: None,
            // cull_mode: None,
            // Setting this to anything other than Fill requires Features::POLYGON_MODE_LINE
            // or Features::POLYGON_MODE_POINT
            polygon_mode: wgpu::PolygonMode::Fill,
            // Requires Features::DEPTH_CLIP_CONTROL
            unclipped_depth: false,
            // Requires Features::CONSERVATIVE_RASTERIZATION
            conservative: false,
        };

        let multisample = wgpu::MultisampleState {
            count: 1,
            mask: !0,
            alpha_to_coverage_enabled: false,
        };
        let multiview = None;

        let vertex = wgpu::VertexState {
            module: &shader,
            entry_point: "vs_main",
            buffers: &[WebgpuMesh::get_vertex_layout()],
        };

        let fragment = wgpu::FragmentState {
            module: &shader,
            entry_point: "fs_main",
            targets: &[Some(wgpu::ColorTargetState {
                format: rhi.borrow().config.format,
                // format: wgpu::TextureFormat::Bgra8UnormSrgb,
                blend: Some(wgpu::BlendState {
                    color: wgpu::BlendComponent {
                        src_factor: wgpu::BlendFactor::SrcAlpha,
                        dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
                        operation: wgpu::BlendOperation::Add,
                    },
                    alpha: wgpu::BlendComponent {
                        src_factor: wgpu::BlendFactor::SrcAlpha,
                        dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
                        operation: wgpu::BlendOperation::Add,
                    },
                }),
                write_mask: wgpu::ColorWrites::ALL,
            })],
        };

        let graphic_pipeline =
            rhi.borrow()
                .device
                .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
                    label: Some("output pass pipeline"),
                    layout: Some(&layout),
                    vertex: vertex,
                    fragment: Some(fragment),
                    primitive: primitive,
                    depth_stencil: None,
                    multisample,
                    multiview,
                });

        let index_buffer =
            rhi.borrow()
                .device
                .create_buffer_init(&wgpu::util::BufferInitDescriptor {
                    label: Some("output_index_buffer"),
                    contents: bytemuck::cast_slice(&OUTPUT_INDEX),
                    usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
                });

        let vertex_buffer =
            rhi.borrow()
                .device
                .create_buffer_init(&wgpu::util::BufferInitDescriptor {
                    label: Some("output_index_buffer"),
                    contents: bytemuck::cast_slice(&OUTPUT_VERTEX),
                    usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
                });

        Self {
            graphic_pipeline,
            vertex_buffer,
            index_buffer,
        }
    }
}

impl OutputPass {
    //

    pub fn draw(
        &mut self,
        encoder: &mut CommandEncoder,
        view: &TextureView,
        groups: &[wgpu::BindGroup],
    ) {
        let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
            label: Some("Canvas Render Pass"),
            color_attachments: &[Some(wgpu::RenderPassColorAttachment {
                view: view,
                resolve_target: None,
                ops: wgpu::Operations {
                    load: wgpu::LoadOp::Clear(wgpu::Color {
                        r: 0.0,
                        g: 0.0,
                        b: 0.0,
                        a: 1.0,
                    }),
                    store: true,
                },
            })],
            depth_stencil_attachment: None,
        });

        render_pass.set_pipeline(&self.graphic_pipeline);

        for group in groups {
            render_pass.set_bind_group(0, group, &[]);

            render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
            render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
            render_pass.draw_indexed(0..OUTPUT_INDEX.len() as u32, 0, 0..1);
        }
    }
}
